//@author A0096466N
//
// **********
//
// logic class is the main processing class
// that takes in the user strings inputs from UI 
// and returns any information required by UI
//
// **********

#include "Logic.h"


// loads data from txt file vector<task> store;
// checks if any data should be archived
// initialises help
Logic::Logic(void) {
	
	ofstream writeFile;
	writeFile.open(FILENAME_LOGGER.c_str());
	writeFile << "Attempting to initialise" << endl;

	vector<string> store;
	_storage.getStorage(store);
	Task task;

	for(vector<string>::iterator iter = store.begin(); iter != store.end(); iter++) {
		task = _parse.convertToTask(*iter);	
		_store.push_back(task);
	}

	autoEditStatus();

	_sortType = DEADLINE;
	_forUIHelp = _help.getHelp();
}

Logic::~Logic(void) {

}

// main function that every user input goes through.
// all exceptions thrown in the program are 
// caught in this main function
// to be feedbacked to the UI
void Logic::start(string userInput) {
	try{
		clearUserEvent();
		clearForUI();	
		_userInput = userInput;
		string command = EMPTY;
		Task task = _parse.convertUserInputToTask(userInput, command);
		
		_userEvent.changeCommand(command);
		_userEvent.changeTask(task);
		
		executeCommand();
	}
	catch(exception & e) {
		_forUIFeedback = e.what();
		_tickOrCross = false;
	}
	
	printList();
	identifyClash();

	return;
}

void Logic::addTask() {
	Add add;
	_forUIFeedback = add.execute(_userEvent.getTask(),_store);
	_history.addToRecords(_userEvent);
	
	// to show only the most recently added task
	_forUIDisplay.clear();
	_forUIDisplay.push_back(_store.back());
	return;
}

void Logic::autoEditStatus() {
	AutoEditStatus autoEditStatus;
	autoEditStatus.execute(_store);
	return;
 } 

void Logic::clearUserEvent() {
	Task task;
	_userEvent.changeCommand(EMPTY);
	_userEvent.changeTask(task); 
	return;
}

void Logic::clearForUI() {
	_forUITaskName.clear();
	_forUIDeadline.clear();
	_forUIPriority.clear();
	_forUITime.clear();
	_forUIStatus.clear();
    _forUICalendar.clear();
	_forUIFeedback = EMPTY;
	_forUIWarning = EMPTY;
	_tickOrCross = true;

	return;
}

void Logic::deleteTask() {
 
    Delete del;
	Task task = _userEvent.getTask();
	_forUIFeedback = del.execute(task,_forUIDisplay, _store);
	_userEvent.changeTask(task);
	_history.addToRecords(_userEvent);
	return;
}

void Logic::display() {
   
	Display display;
	Task task = _userEvent.getTask();
  
	_forUIDisplay = display.execute(task.getTaskName(),_store);

	if(_forUIDisplay.size() == NIL) {
		_forUIFeedback = DISPLAY_EMPTY_FEEDBACK;
	} else {
		_forUIFeedback = DISPLAYING_FEEDBACK;
		if(task.getTaskName() != DEFAULT) {
			_forUIFeedback += task.getTaskName();
		}
	}
	_sortType = DEADLINE;
	return;
}

void Logic::executeCommand() {
			
	string command = _userEvent.getCommand();
	
	if (command == ADD) {
		addTask();	
	
	} else if (command == DISPLAY) {
		display();

	} else if (command == MODIFY) {					
		modify();		

	} else if (command == DELETE_) {
		deleteTask();

	} else if (command == SEARCH) {
		search();

	} else if (command == RESET) {
		reset();
	
	} else if (command == REDO) {
		redo();

	} else if (command == UNDO) {
		undo();
	
	} else if (command == REMINDER) {
		reminder();
		
	} else if(command == MARK) {
		mark();

	} else if(command == FILTER) {
		filter();
	
	} else if(command == SORT) {
		sort();

	} else {
		invalidCommand();
	}
	
	return;
}

void Logic::identifyClash() {
	
	// only when more than 1 can there be a clash
	if(_forUIDisplay.size() > 1) {
		Clash identifyClash;
		_forUIWarning = identifyClash.execute(_forUIDisplay);
	}

	return;
}

// when invalid command, try to search whatever the user typed
void Logic::invalidCommand() {
	
	Task task;
	task.changeTaskName(_userInput);
	_userEvent.changeTask(task);

	try {
		search();
	}
	catch (exception & e) {
		throw exception ("Invalid command entered. Press F1 for help");
	}

	if(_forUIDisplay.size() != NIL) {
		throw exception("Invalid command entered, but perhaps you were trying to search for this? For help, press F1");
	}

	throw exception("Invalid command entered. Press F1 for help");
}

void Logic::filter(){
	
	if(_forUIDisplay.size() == NIL) {
		_forUIFeedback = FILTER_EMPTY_FEEDBACK;
		return;
	}

	Filter filter;
	Task task = _userEvent.getTask();
	_forUIDisplay = filter.execute(task.getTaskName(), _forUIDisplay);
	_forUIFeedback = FILTER_FEEDBACK1;
	_forUIFeedback += task.getTaskName();
	_forUIFeedback += FILTER_FEEDBACK2;
	return;
}

void Logic::mark() {
	
	Mark mark;
	Task task = _userEvent.getTask();
	
	_forUIFeedback = mark.execute(task,_forUIDisplay,_store);
	
	_userEvent.changeTask(task);
	_history.addToRecords(_userEvent);

	return;
}

void Logic::modify() {
	
	Modify modify;
	Task task = _userEvent.getTask();
	
	_forUIFeedback = modify.execute(task,_forUIDisplay,_store);
	
	_userEvent.changeTask(task);
	_history.addToRecords(_userEvent);
	
	return;
}

void Logic::printList() {
	
	if(!_forUIDisplay.empty()) {
		sortList();
	}

	// splitting tasks into the various elements
	// e.g. taskname,deadline,etc.
	for(unsigned int i = 0; i < _forUIDisplay.size(); i++) {
		_forUITaskName.push_back(_forUIDisplay[i].getTaskName());
		_forUIDeadline.push_back(_forUIDisplay[i].getDeadline());
		_forUIPriority.push_back(_forUIDisplay[i].getPriority());
		_forUITime.push_back(_forUIDisplay[i].getTime());
		_forUIStatus.push_back(_forUIDisplay[i].getStatus());
	}

	// getting the actual date of each task
	// in order to bold calendar in UI
	for(unsigned int i = 0; i < _store.size(); i++) {
		if(_store[i].getDateInSeconds() != NIL) {
			int day;
			int month;
			int year;

			time_t seconds = _store[i].getDateInSeconds();

			struct tm * time;
			time = localtime(&seconds);
	
			day = (*time).tm_mday;
			month = (*time).tm_mon + 1;
			year = (*time).tm_year + 1900;

			string data;
			data = to_string(day);
			data += " " + to_string(month);
			data += " " + to_string(year);

			_forUICalendar.push_back(data);
		}
	}
	return;
}

void Logic::redo() {
	
	Redo redo;
	_forUIFeedback = redo.execute(_history,_forUIDisplay,_store);

	return;
}

void Logic::reminder() {
	
	Reminder reminder;
	
	_forUIDisplay = reminder.execute(_store);
	
	if(_forUIDisplay.size() == NIL) {
		_forUIFeedback = REMINDER_NIL_FEEDBACK;
	} else {
		_forUIFeedback = REMINDER_FEEDBACK;
	}

	_sortType = DEADLINE;
	return;
}

void Logic::reset() {
	
	Reset reset;

	_history.addToRecords(_userEvent);
	_history.addStorage(_store);
	
	_forUIFeedback = reset.execute(_forUIDisplay,_store);
	
	return;
 }
 
void Logic::search() {
	
	Searcher search;
	string toBeSearched;
	Task task = _userEvent.getTask();

	if(task.getTaskName() != EMPTY) {
		toBeSearched = task.getTaskName();
		_forUIDisplay = search.execute(toBeSearched,_store);	
		
	} else {
		throw exception("You are searching for nothing");
	}

	if(_forUIDisplay.size() == NIL) {
		_forUIFeedback = SEARCH_NIL_FEEDBACK;
	} else {
		_forUIFeedback = SEARCH_FEEDBACK;
	}

	return;
}

void Logic::sort() {
	
	Task task = _userEvent.getTask();
	string type = task.getTaskName();

	if(type == PRIORITY || type == DEADLINE || type == TIME || type == TASK) {
		_sortType = type;
		_forUIFeedback = SORT_FEEDBACK + _sortType;
	} else {
		throw exception ("Invalid sort type");
	}

	sortList();
}

void Logic::sortList() {
	
	if(_forUIDisplay.size() == NIL) {
		_forUIFeedback = SORT_NIL_FEEDBACK;
		return;
	}
	
	Sorter sort;
	if(_sortType == DEADLINE) {
		_forUIDisplay = sort.start(_forUIDisplay);
	} else {
		_forUIDisplay = sort.start(_sortType,_forUIDisplay);
	}
	
	return;
}

void Logic::undo() {
	
	Undo undo;
	_forUIFeedback = undo.execute(_history,_forUIDisplay,_store);
	
	return;
}

bool Logic::getTickOrCross() {
	return _tickOrCross;
}

vector <string> Logic::getForUITaskName() {
	return _forUITaskName;
}

vector <string> Logic::getForUIDeadline() {
	return _forUIDeadline;
}

vector <string> Logic::getForUIHelp() {
	return _forUIHelp;
}

vector <string> Logic::getForUIPriority() {
	return _forUIPriority;
}

vector <string> Logic::getForUITime() {
	return _forUITime;
}

vector <string> Logic::getForUIStatus() {
	return _forUIStatus;
}

vector <string> Logic::getForUICalendar() {
	return _forUICalendar;
}

string Logic::getForUIFeedback() {
	return _forUIFeedback;
}

string Logic::getForUIWarning() {
	return _forUIWarning;
}

// for unit testing
vector <Task> Logic::getForUIDisplay() {
	return _forUIDisplay;
}